﻿#include "adtcp.h"
#include <QDebug>
#include<QVariant>
#pragma execution_character_set("utf-8")
// QTcpSocket会自动处理大小端问题

AdTcp::AdTcp(QListWidget *_client_lw, QObject *parent) :
    QTcpServer(parent)
{
    //注册tcp_MSG类型
    qRegisterMetaType<tcp_MSG>("tcp_MSG");

    // 监听任意地址8888端口
    if(!listen(QHostAddress::Any, 8888))
    {
        //QMessageBox::warning(this, "服务器启动失败");
        close();
    }


    client_list = new QList<Client *>;
    client_lw =_client_lw;
    //设置多选项
    client_lw->setSelectionMode(QAbstractItemView::ExtendedSelection);

    // 每当有新的连接可用
    connect(this, SIGNAL(newConnection()), this, SLOT(newClient()));
    qDebug()<<"init tcp";
  //  connect(this, SIGNAL(), this, SLOT(newClient()));
  //  connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(newClient()));
}

AdTcp::~AdTcp()
{
    delete client_list;
    close();
}

// 新的客户端连接
void AdTcp::newClient()
{
    Client *new_client = new Client;
    // 与客户端链接,动态创建socket对象
    new_client->msocket = this->nextPendingConnection();// 等待连接的//作为已连接的QTcpSocket对象，返回下一个挂起连接

    /* 每当有新的输入数据时，就会发出这个信号。
    请记住，新传入的数据只报告一次;如果您不读取所有数据，这个类会缓冲数据，您可以稍后读取它，但是除非新数据到达，否则不会发出信号。*/
    connect(new_client->msocket, SIGNAL(readyRead()), this, SLOT(read_back()));

    // 该信号在套接字断开连接时发出
    connect(new_client->msocket, SIGNAL(disconnected()), this, SLOT(rmClient()));
    client_list->append(new_client);
}

//读取客户端上传ID
void AdTcp::read_back()
{
    // 返回此信号的 发送对象
    QTcpSocket *getSocket=qobject_cast<QTcpSocket *>(sender());
    //读取缓冲区数据
    QByteArray  buffer = getSocket->readAll();//client_list->last()->msocket->readAll();// 读取最后客户端(也就是最新的)
    tcp_backMSG *msg=(tcp_backMSG *)buffer.data();        //强转为结构体，需要用结构体指针接收
    qDebug()<<"消息类型"<<msg->type;

    if(msg->type==MsgType::Init&&msg->state==0)
    {
        QString id(msg->id);
        client_list->last()->id = id;
        client_lw->addItem(id);// 添加项到 客户端列表（下位机）
    }
    else if(msg->type==MsgType::Write_back&&msg->state==1)
    {
        qDebug()<<"收到回复";
        // 记录开始时间
        QTime startTime = QTime::currentTime();

        getSocket->write(sendImage);
        // 用于等待发送的数据被写入到网络套接字描述符中，这样就可以确保数据完全被发送出去，
        //该函数会阻塞程序继续执行，直到数据被完全发送出去
        if (getSocket->waitForBytesWritten())
        {
            getSocket->flush(); //释放socket缓存
        }
        // 记录结束时间
        QTime endTime = QTime::currentTime();
        // 计算运行时间
        int milsec = startTime.msecsTo(endTime);
        qDebug()<<"发送消耗时间"<<milsec<<"毫秒";
    }
}

// 文字 广播下发
void AdTcp::broadcastMsg(tcp_MSG msg)
{
    QByteArray  sendTcpData;
    //使用字节数组,将结构体转为字符数组，发送的是字符数组（数据在传输过程中都是byte类型的）
    //直接sizeof（senddata）内存会变小，设置了对齐方式解决
    sendTcpData.resize(sizeof(tcp_MSG));

    //将封装好的结构体转为QByteArray数组，因为传输都是Byte类型
    memcpy(sendTcpData.data(),&msg,sizeof(tcp_MSG));
    int count = client_list->size();
    qDebug()<<"广播下发数量："<<count;
    for(int i = 0; i< count; i++)
    {
        client_list->at(i)->msocket->write(sendTcpData);//往套接字缓存中写入数据，并发送
        client_list->at(i)->msocket->flush(); //释放socket缓存
    }
}

// 天气 多选单播下发
void AdTcp::MultiSelectUnicastMsg(tcp_MSG msg)
{
    QByteArray  sendTcpData;
    //使用字节数组,将结构体转为字符数组，发送的是字符数组（数据在传输过程中都是byte类型的）
    //直接sizeof（senddata）内存会变小，设置了对齐方式解决
    sendTcpData.resize(sizeof(tcp_MSG));

    //将封装好的结构体转为QByteArray数组，因为传输都是Byte类型
    memcpy(sendTcpData.data(),&msg,sizeof(tcp_MSG));

    if(client_list->size()!=client_lw->count())
    {
        emit GUI_WarningSignal("提示","客户端存储数量出错",NULL,NULL);
        return;
    }
    QList<QListWidgetItem *> selectedItems = client_lw->selectedItems();
    for (auto item : selectedItems)
    {
        int index = client_lw->row(item);
        qDebug() << "选定 item:" << item->text();
        client_list->at(index)->msocket->write(sendTcpData);
        client_list->at(index)->msocket->flush();
    }
}

// 广告发送操作
void AdTcp::Ad_SendAction(int action, QString path, int index, int allAd_Num)
{
    QByteArray  sendTcpData;
    //使用字节数组,将结构体转为字符数组，发送的是字符数组（数据在传输过程中都是byte类型的）
    //直接sizeof（senddata）内存会变小，设置了对齐方式解决
    sendTcpData.resize(sizeof(tcp_MSG));
    tcp_MSG msg={};
    msg.type=action;

    QFileInfo file(path);
    strcpy(msg.fileName,file.fileName().toUtf8().data());

    msg.index=index;
    msg.allAd_Num=allAd_Num;

    if(action==MsgType::AD_add)
    {
        QImage image(path);
        QByteArray byteArray;
        QBuffer buffer(&byteArray);
        buffer.open(QIODevice::WriteOnly);
        //获取文件的后缀名，并将其转换为大写字母
        qDebug()<<"图片格式："<<file.suffix().toUpper().toStdString().c_str();
        image.save(&buffer,file.suffix().toUpper().toStdString().c_str()); //将图片保存为PNG/JPG等格式

        sendImage = byteArray.toBase64();
         qDebug()<<"图片size:"<<sendImage.size();
        msg.fileSize=sendImage.size();
        buffer.close();
    }
    else if(action==MsgType::AD_delete)
    {

    }

    //将封装好的结构体转为QByteArray数组，因为传输都是Byte类型
    memcpy(sendTcpData.data(),&msg,sizeof(tcp_MSG));

    QList<QListWidgetItem *> selectedItems = client_lw->selectedItems();
    for (auto item : selectedItems)
    {
        int index = client_lw->row(item);
        qDebug() << "发送 item:" << item->text();
        client_list->at(index)->msocket->write(sendTcpData);
        client_list->at(index)->msocket->flush();
    }
}

// 删除客户端
void AdTcp::rmClient()
{
    qDebug()<<"客户端断开";
    // 返回此信号的 发送对象
    QTcpSocket *getSocket=qobject_cast<QTcpSocket *>(sender());

//    if(client_list->first()->msocket->isSequential())
//    {
//        client_lw->clear();
//    }
    for(int i=0;i<client_list->count();i++)
    {
        if(getSocket==client_list->at(i)->msocket)
        {
            if(client_list->count()==client_lw->count())
            {
                QListWidgetItem *item=client_lw->takeItem(i);//删除该列表项
                delete item;//手工再释放该列表项占用的资源
            }
            client_list->takeAt(i);
            getSocket->deleteLater();//延时释放
        }
    }
}
